#include <myheader.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <arpa/inet.h>
#define TIME_OUT 1000
#define EPOLL_ARR_SIZE 100
typedef struct conn_s
{
    int fd;
    time_t conn_time;
}conn_t;

/*Usage:  */
int main(int argc, char *argv[])
{
    //创建套接字
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    ERROR_CHECK(sfd,-1,"socket");

    //端口复用
     //reuse port
    int optval = 1;
    int ret = setsockopt(sfd,SOL_SOCKET,SO_REUSEPORT | SO_REUSEADDR,
                             &optval,sizeof(optval));
    ERROR_CHECK(ret,-1,"setsockopt");

    //绑定端口号和ip地址
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    addr.sin_port = htons(8080);

    ret = bind(sfd,(struct sockaddr *)&addr,sizeof(addr));
    ERROR_CHECK(ret,-1,"bind");

    //监听套接字，并且指定最大并发数量为10000
    ret = listen(sfd,10000);
    ERROR_CHECK(ret, -1, "listen");

    //创建epoll示例
    int efd = epoll_create1(0);
    ERROR_CHECK(efd, -1, "epoll_create");

    //eopll_ctl，监听epoll的读事件
    struct epoll_event event;
    memset(&event, 0, sizeof(event));
    event.events = EPOLLIN;//监听读事件
    event.data.fd = sfd;
    ret = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
    ERROR_CHECK(ret, -1, "epoll_ctl");

    //设置最大连接数量数组
    struct conn_s list[100];
    int listsize = 0;
    memset(list, 0, sizeof(list));


    //启动事件循环，设置一个数组用于存放已经就绪的文件描述符，nready个
    struct epoll_event events[EPOLL_ARR_SIZE] = {0};
    while(1)
    {
        int nready = epoll_wait(efd, events, EPOLL_ARR_SIZE, TIME_OUT);

        //处理数据之前要先判断是否超时，超时就踢出
        for(int i = 0; i < listsize; ++i)
        {
            time_t time_now = time(NULL);
            if((time_now - list[i].conn_time) > 10)
            {
                printf("kick off\n");
                //踢出epoll
                ret = epoll_ctl(efd, EPOLL_CTL_DEL, list[i].fd, NULL);
                ERROR_CHECK(ret, -1, "epoll ctl kickoff");
                close(list[i].fd);

                //从list中删除
                for(int j = i; j < listsize; ++j)
                {
                    list[j] = list[j + 1];
                }
                listsize --;
            }
        }

        if(nready == -1)
        {
            continue;
        }
        else if(nready == 0) 
        {
            //没有响应
            printf("epoll timeout\n");
        }
        else
        {
            printf("nready = %d\n", nready);
            //表示有描述符响应，遍历events数组，处理文件描述符
            for(int i = 0; i < nready; ++i)
            {
                //取出响应的fd
                int fd = events[i].data.fd;

                if(fd == sfd)
                {
                    printf("new client connect!\n");
                    //1.表述有新的连接进来，accept后建立新的专门用于通信的文件对象，
                    struct sockaddr_in client_addr;
                    memset(&client_addr,0,sizeof(client_addr));
                    socklen_t len = sizeof(client_addr);
                    int netfd = accept(sfd, (struct sockaddr *)&client_addr, &len);
                    ERROR_CHECK(netfd, -1, "accept");

                    //2.然后加入到epoll中进行监听
                    event.data.fd = netfd;
                    event.events = EPOLLIN;
                    ret = epoll_ctl(efd, EPOLL_CTL_ADD, netfd, &event);
                    ERROR_CHECK(ret, -1, "epoll_ctl netfd");

                    //并放入到list当中
                    struct conn_s conn;
                    conn.fd = netfd;
                    time_t time_now;
                    time(&time_now);
                    conn.conn_time = time_now;
                    list[listsize] = conn;
                    listsize ++;

                }
                else
                {
                    printf("client send message!\n");
                    //表示有新的数据到来，处理数据,将数据群发
                    char buf[1024] = {0};
                    memset(buf, 0, sizeof(buf));

                    int read_cnt = recv(fd, buf, sizeof(buf) - 1, 0);
                    ERROR_CHECK(read_cnt, -1, "recv");
                    buf[read_cnt] = 0;

                    if(read_cnt == 0)
                    {
                        //表示断开连接
                        ret = epoll_ctl(efd, EPOLL_CTL_DEL, fd, NULL);
                        ERROR_CHECK(ret, -1, "epoll_ctl delete");

                        //并且从list中删除
                        //delete the conn in list
                        for(int i = 0; i < listsize; i++)
                        {
                            if(list[i].fd == fd)
                            {
                                for(int j = i; j < listsize - 1; j++)
                                {
                                    list[j] = list[j+1];
                                }
                            }
                        }
                        listsize --;
                        close(fd);

                    }
                    else if(read_cnt == -1)
                    {
                        perror("read error");
                    }
                    else
                    {
                        for(int i = 0; i < listsize; ++i)
                        {
                            if(list[i].fd == fd || list[i].fd == sfd)
                            {
                                //重置时间
                                time_t time_now;
                                time(&time_now);
                                list[i].conn_time = time_now;
                                continue;
                            }
                            //群发
                            printf("client send to buf :%s\n", buf);
                            int send_cnt = send(list[i].fd, buf,strlen(buf), 0);
                            ERROR_CHECK(send_cnt, -1, "group_send");
                        }
                    }


                }

            }
        }

    }
    close(sfd);
    close(efd);
    return 0;
}

